from typing import List

import torch
import numpy as np
import pandas as pd
import math

# 实验课也分为三种
experiment_course_tensors = [
    torch.tensor([0, 32]),  # 大课
    torch.tensor([1, 16]),  # 中课
    torch.tensor([2, 8])  # 小课
]

# 每一周排课的方法总共有三种
course_schedule_tensors = [
    torch.tensor([0, 2]),  # 短课
    torch.tensor([1, 4]),  # 中课
    torch.tensor([2, 8])  # 长课
]


# 如何设计合理的位图来应对复杂多变的情况？
# 解决方案：按照 课程类型 进行位图设计，暂时不改动 255 bitmap

# # 利用强化学习的模型确定 weight 的比例，初始化为 4:2:2:2，要调！
def reinforcement_learning_model():
    weight = {
        'scv_weight': 0.4,
        'weekend_weight': 0.2,
        'efficient_weight': 0.2,
        'priority_weight': 0.2
    }
    return weight


def judge_schedule(schedule_tensor, priority_of_schedule):
    weight = reinforcement_learning_model()
    # 获取硬编码参数 hsv，前后端的同学已经规避了 hsv，所以这个参数可以考虑一票否决
    result = []

    # 求对应的比重
    scv = scv_model(schedule_tensor, priority_of_schedule)
    print(scv)
    weekend = weekend_model(schedule_tensor, priority_of_schedule)
    print(weekend)
    efficient = efficient_model(schedule_tensor, priority_of_schedule)
    print(efficient)
    priority = priority_model(schedule_tensor, priority_of_schedule)
    print(priority)

    for i in range(len(priority_of_schedule)):
        # print(i, scv[i], weekend[i], efficient[i], priority[i])
        # 参数比重相乘取最大值并返回结果
        value = scv[i] * weight['scv_weight'] + weekend[i] * weight['weekend_weight'] + efficient[i] * weight['efficient_weight'] + priority[i] * weight['priority_weight']
        result.append(value)
        print(i, result[i])  # 打印评分结果


def scv_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 60  # 基准分是及格

        if row['Course_Sequence'][-1] == "12":
            point -= 5  # 如果第 12 节课要上课，扣分

        if (int)(row['Course_Sequence'][0]) % 2 == 1:
            point += 10  # 优先考虑 1 3 5 7 9 开头的课

        day = row['Day']

        course_dense = calculateDenseOfSchedule(day, schedule_tensor)  # 把这一天的安排加入到原来的课表中，计算课程密度，课程密度越高越好
        point += course_dense * 10  # 当天课程密度越高，分数越高

        # 课程的排列是平均分配的，不考虑周末的情况
        # 假如说我们可以获取一周之内的课程的分布情况，那么可以计算出课程在工作日平均分布情况
        list_of_courses_per_week = [5, 5, 5, 5, 5, 0, 0]
        # 求标准差，然后进行标准差赋值
        # 计算平均值
        average = sum(list_of_courses_per_week) / 5
        # 计算每个数值与平均值的差的平方
        squared_diffs = [(x - average) ** 2 for x in list_of_courses_per_week]
        # 计算方差
        variance = sum(squared_diffs) / 4  # 使用 4 作为除数进行样本方差的计算
        # 计算标准差
        standard_deviation = math.sqrt(variance)
        point += standard_deviation_model(standard_deviation)  # 标准差的分数，这个函数需要根据实际情况进行设计

        sorted_score = 0  # 排序分数，排序分数越高，说明越有序

        # 周数越连续分数越高
        for i in range(1, len(row['Week'])):
            if ord(row['Week'][i]) - ord(row['Week'][i-1]) == 1:
                sorted_score += 2
            elif ord(row['Week'][i]) - ord(row['Week'][i-1]) == 2:
                sorted_score += 1
            else:
                sorted_score += 0.5

        sorted_score /= len(row['Week'])

        if sorted_score > 0:
            point += sorted_score * 10

        result.append(point)

    return result


def standard_deviation_model(standard_deviation: int) -> int:
    return standard_deviation * 5


# 计算软编码参数 scv，scv 可以用很多方法进行复杂地设计

def calculateDenseOfSchedule(day, schdule_tensor) -> int:
    total_courses_per_day = 0  # 假如说我们已经求了课程密度
    course_dense = total_courses_per_day / 12
    return course_dense


def weekend_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 100
        day = row['Day']
        if day == 6 or day == 7:
            point -= 50
        result.append(point)

    return result


def efficient_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 100
        efficiency = int(row['Number_Of_Students']) / int(row['Volume'])
        result.append(point * efficiency)
    return result


def priority_model(schedule_tensor, priority_of_schedule) -> List[int]:
    result = []
    for index, row in priority_of_schedule.iterrows():
        point = 50
        priority = row['Application_Priority']
        if priority == "1":
            point += 50
        elif priority == "2":
            point += 30
        elif priority == "3":
            point += 10
        result.append(point)
    return result


# 考虑数据结构还有老师的评价，考虑和后端同学沟通
# 从而实现一个不断完善的过程！
# 要留一个反馈优化的空间

def reply():
    pass

if __name__ == '__main__':
    # 根据 .csv 数据 获取 课程信息
    # 1. 项目组可以根据老师的姓名以及工号获取到老师教学班的课程信息，我们可以知道他所教的班级在特定时间段是否有课
    # 如果这一个老师教授多个班级，那么前后端的同学可以考虑直接按照老师视角把合并后的课表再传递给算法端
    # 横向：表示星期几，纵向：表示第几节课，内容：（哪一周有课，哪一周没课）
    schedule_without_experiment = pd.read_csv('../data/schedule_easy_without_experiment.CSV', delimiter=',', header=0)

    # 2. 获取老师的志愿，我们先暂时用 .csv 进行表示，事实上是可以从前后端交互的数据库中获取的
    priority_of_schedule = pd.read_csv('../data/priority.CSV', delimiter=',', header=0)

    # 3. 获取网络训练的原始数据
    # training_data = pd.read_csv('../data/training_data.csv', delimiter=',', header=0)

    # 既然前后端的同学可以保证老师的所有志愿都不冲突，算法岗位的同学就默认老师给的方案“可行”了

    # 利用 torch 的张量构造一个课表张量
    # 设置张量的形状
    weeks = 20 + 1  # 我们假设有 20 周，规避零一问题
    days_per_week = 7 + 1
    classes_per_day = 12 + 1

    # 初始化一个全零的张量（ 一开始没有课程 ）
    schedule_tensor = torch.zeros(weeks, days_per_week, classes_per_day)

    # 将 schedule_without_experiment 翻译成 schedule_tensor 信息
    # 将 课表原有的 CSV数据 转换为张量，规避了零一问题，直接输入周数 — 天数 — 节数 就可以判定这节课有没有被占用
    for time_slot in range(classes_per_day - 1):  # 对于每天的每个时间段
        for day_index in range(1, days_per_week):  # 对于一周中的每一天
            value = schedule_without_experiment.iloc[time_slot].iloc[day_index]  # 我们从 csv 文件中用 iloc 函数获取数据

            if value != 0 and value != "0":  # 如果有课
                if isinstance(value, str):  # 如果这一段时间课很多，原来用字符串表示
                    # 将字符串转换为整数列表
                    weeks_with_class = list(map(int, value.split(', ')))
                    # print(weeks_with_class)
                else:
                    weeks_with_class = [value]
                # 更新张量
                for week in weeks_with_class:
                    if week <= weeks:  # 确保周数不超过张量的维度
                        schedule_tensor[week, day_index, time_slot + 1] = 1

    judge_schedule(schedule_tensor, priority_of_schedule)